﻿using System;
using System.IO;
using TMPro;
using UnityEngine;
using UnityEngine.UI;

namespace Fusion.Frameworks.Assets
{
    public class AssetsUtility
    {
        /// <summary>
        /// 设置字体材质
        /// </summary>
        /// <param name="gameObject"></param>
        /// <param name="path"></param>
        public static void SetMaterialOfTextMeshProUGUI(GameObject gameObject, string path)
        {
            TextMeshProUGUI text = gameObject.GetComponent<TextMeshProUGUI>();
            SetMaterial(text, path);
        }
        public static void SetMaterial(TextMeshProUGUI text, string path)
        {
            Material material = AssetsManager.Instance.Load<Material>(path);
            text.fontMaterial = material;
            GameObjectAssetRecorder assetRecorder = text.gameObject.GetOrAddComponent<GameObjectAssetRecorder>();
            assetRecorder.Record(AssetsConfig.GetAssetBundleName(path));
        }

        /// <summary>
        /// 异步设置字体材质
        /// </summary>
        /// <param name="gameObject"></param>
        /// <param name="path"></param>
        public static void SetMaterialOfTextMeshProUGUIAsync(GameObject gameObject, string path)
        {
            TextMeshProUGUI text = gameObject.GetComponent<TextMeshProUGUI>();
            SetMaterialAsync(text, path);
        }
        public static void SetMaterialAsync(TextMeshProUGUI text, string path)
        {
            string assetAsyncKey = text.GetInstanceID().ToString() + "_material";
            Action<UnityEngine.Object> callback = delegate (UnityEngine.Object asset)
            {
                text.fontMaterial = asset as Material;
            };

            HandleAsync<Material>(text.gameObject, path, assetAsyncKey, callback);
        }

        /// <summary>
        /// 设置音频源的音频文件
        /// </summary>
        /// <param name="audioSource"></param>
        /// <param name="path"></param>
        public static void SetAudioSourceClip(AudioSource audioSource, string path)
        {
            AudioClip clip = AssetsManager.Instance.Load<AudioClip>(path);
            audioSource.clip = clip;
            GameObjectAssetRecorder assetRecorder = audioSource.gameObject.GetOrAddComponent<GameObjectAssetRecorder>();
            assetRecorder.Record(AssetsConfig.GetAssetBundleName(path));
        }

        /// <summary>
        /// 异步设置音频源的音频文件
        /// </summary>
        /// <param name="audioSource"></param>
        /// <param name="path"></param>
        public static void SetAudioSourceClipAsync(AudioSource audioSource, string path, Action finishCallback = null)
        {
            string assetAsyncKey = audioSource.GetInstanceID().ToString() + "_clip";

            Action<UnityEngine.Object> callback = delegate (UnityEngine.Object asset)
            {
                audioSource.clip = asset as AudioClip;
                if (finishCallback != null)
                {
                    finishCallback();
                }
            };

            HandleAsync<AudioClip>(audioSource.gameObject, path, assetAsyncKey, callback);
        }

        /// <summary>
        /// 设置精灵图片
        /// </summary>
        /// <param name="gameObject"></param>
        /// <param name="path"></param>
        public static void SetSpriteOfSpriteRenderer(GameObject gameObject, string path)
        {
            SpriteRenderer spriteRenderer = gameObject.GetComponent<SpriteRenderer>();
            SetSprite(spriteRenderer, path);
        }
        public static void SetSprite(SpriteRenderer spriteRenderer, string path)
        {
            Sprite sprite = AssetsManager.Instance.Load<Sprite>(path);
            spriteRenderer.sprite = sprite;

            GameObjectAssetRecorder assetRecorder = spriteRenderer.gameObject.GetOrAddComponent<GameObjectAssetRecorder>();
            assetRecorder.Record(AssetsConfig.GetAssetBundleName(path));
        }

        /// <summary>
        /// 异步设置精灵图片
        /// </summary>
        /// <param name="gameObject"></param>
        /// <param name="path"></param>
        public static void SetSpriteOfSpriteRendererAsync(GameObject gameObject, string path)
        {
            SpriteRenderer spriteRenderer = gameObject.GetComponent<SpriteRenderer>();
            SetSpriteAsync(spriteRenderer, path);
        }
        public static void SetSpriteAsync(SpriteRenderer spriteRenderer, string path)
        {
            spriteRenderer.enabled = false;
            string assetAsyncKey = spriteRenderer.GetInstanceID().ToString() + "_sprite";

            Action<UnityEngine.Object> callback = delegate (UnityEngine.Object asset)
            {
                spriteRenderer.enabled = true;
                spriteRenderer.sprite = asset as Sprite;
            };
            HandleAsync<Sprite>(spriteRenderer.gameObject, path, assetAsyncKey, callback);
        }

        /// <summary>
        /// 设置精灵图片
        /// </summary>
        /// <param name="gameObject"></param>
        /// <param name="path"></param>
        public static void SetSpriteOfImage(GameObject gameObject, string path)
        {
            Image image = gameObject.GetComponent<Image>();
            SetSprite(image, path);
        }

        public static void SetSprite(Image image, string path)
        {
            Sprite sprite = AssetsManager.Instance.Load<Sprite>(path);
            image.sprite = sprite;

            GameObjectAssetRecorder assetRecorder = image.gameObject.GetOrAddComponent<GameObjectAssetRecorder>();
            assetRecorder.Record(AssetsConfig.GetAssetBundleName(path));
        }

        /// <summary>
        /// 异步设置精灵图片
        /// </summary>
        /// <param name="gameObject"></param>
        /// <param name="path"></param>
        public static void SetSpriteOfImageAsync(GameObject gameObject, string path)
        {
            Image image = gameObject.GetComponent<Image>();
            SetSpriteAsync(image, path);
        }

        public static void SetSpriteAsync(Image image, string path)
        {
            image.enabled = false;
            string assetAsyncKey = image.GetInstanceID().ToString() + "_sprite";

            Action<UnityEngine.Object> callback = delegate (UnityEngine.Object asset)
            {
                image.enabled = true;
                image.sprite = asset as Sprite;
            };

            HandleAsync<Sprite>(image.gameObject, path, assetAsyncKey, callback);
        }

        /// <summary>
        /// 设置MeshRenderer的材质
        /// </summary>
        /// <param name="gameObject"></param>
        /// <param name="path"></param>
        public static void SetMaterialOfMeshRenderer(GameObject gameObject, string path)
        {
            MeshRenderer meshRenderer = gameObject.GetComponent<MeshRenderer>();
            SetMaterial(meshRenderer, path);
        }

        public static void SetMaterial(MeshRenderer meshRenderer, string path)
        {
            Material material = AssetsManager.Instance.Load<Material>(path);
            meshRenderer.material = material;
            GameObjectAssetRecorder assetRecorder = meshRenderer.gameObject.GetOrAddComponent<GameObjectAssetRecorder>();
            assetRecorder.Record(AssetsConfig.GetAssetBundleName(path));
        }

        /// <summary>
        /// 异步设置MeshRenderer的材质
        /// </summary>
        /// <param name="gameObject"></param>
        /// <param name="path"></param>
        public static void SetMaterialOfMeshRendererAsync(GameObject gameObject, string path)
        {
            MeshRenderer meshRenderer = gameObject.GetComponent<MeshRenderer>();
            SetMaterialAsync(meshRenderer, path);
        }

        public static void SetMaterialAsync(MeshRenderer meshRenderer, string path)
        {
            string assetAsyncKey = meshRenderer.GetInstanceID().ToString() + "_material";
            Action<UnityEngine.Object> callback = delegate (UnityEngine.Object asset)
            {
                meshRenderer.material = asset as Material;
            };

            HandleAsync<Material>(meshRenderer.gameObject, path, assetAsyncKey, callback);
        }

        private static void HandleAsync<T>(GameObject gameObject, string path, string assetAsyncKey, Action<UnityEngine.Object> callback) where T : UnityEngine.Object
        {
            AssetAsyncHandler assetAsyncHandler = gameObject.GetOrAddComponent<AssetAsyncHandler>();

            assetAsyncHandler.RegisterCallback(assetAsyncKey, callback);

            AssetsManager.Instance.LoadAsync(path, delegate (T asset)
            {
                if (assetAsyncHandler != null)
                {
                    Action<T> currentCallback = assetAsyncHandler.GetCallback(assetAsyncKey);
                    if (callback == currentCallback)
                    {
                        currentCallback(asset);
                        assetAsyncHandler.RemoveCallback(assetAsyncKey);
                    }
                    GameObjectAssetRecorder assetRecorder = gameObject.GetOrAddComponent<GameObjectAssetRecorder>();
                    assetRecorder.Record(AssetsConfig.GetAssetBundleName(path));
                }
                else
                {
                    Debug.Log($"The {gameObject.name} has been destroyed, associated async asset {path} was loaded and will be released now.");
                    Release(path);
                }
            });


        }

        /// <summary>
        /// 通过预设创建新的GameObject
        /// </summary>
        /// <param name="path"></param>
        /// <param name="parent"></param>
        /// <param name="worldPositionStays"></param>
        /// <returns></returns>
        public static GameObject CreateGameObject(string path, GameObject parent = null, bool worldPositionStays = false)
        {
            GameObject originGameObject = AssetsManager.Instance.Load<GameObject>(path);
            GameObject gameObject = UnityEngine.Object.Instantiate(originGameObject);
            gameObject.name = gameObject.name.Replace("(Clone)", "");
            if (parent != null)
            {
                gameObject.transform.SetParent(parent.transform, worldPositionStays);
            }

            GameObjectAssetRecorder assetRecorder = gameObject.GetOrAddComponent<GameObjectAssetRecorder>();
            assetRecorder.Record(AssetsConfig.GetAssetBundleName(path));
            return gameObject;
        }

        /// <summary>
        /// 异步通过预设创建新的GameObject
        /// </summary>
        /// <param name="path"></param>
        /// <param name="parent"></param>
        /// <param name="worldPositionStays"></param>
        /// <param name="finishCallback"></param>
        public static void CreateGameObjectAsync(string path, GameObject parent = null, bool worldPositionStays = false, Action<GameObject> finishCallback = null)
        {
            AssetsManager.Instance.LoadAsync<GameObject>(path, delegate (GameObject originGameObject)
            {
                GameObject gameObject = UnityEngine.Object.Instantiate(originGameObject);
                gameObject.name = gameObject.name.Replace("(Clone)", "");
                if (parent != null)
                {
                    gameObject.transform.SetParent(parent.transform, worldPositionStays);
                }

                GameObjectAssetRecorder assetRecorder = gameObject.GetOrAddComponent<GameObjectAssetRecorder>();
                assetRecorder.Record(AssetsConfig.GetAssetBundleName(path));
                if (finishCallback != null)
                {
                    finishCallback(gameObject);
                }
            });
        }


        /// <summary>
        /// 清理GameObject上所有的资源
        /// </summary>
        /// <param name="gameObject"></param>
        public static void ReleaseAssets(GameObject gameObject)
        {
#if FUSION_ASSETBUNDLE || !UNITY_EDITOR
            GameObjectAssetRecorder[] assetRecorders = gameObject.GetComponentsInChildren<GameObjectAssetRecorder>(true);
            for (int index = 0; index < assetRecorders.Length; index++)
            {
                assetRecorders[index].Release();
            }
#else
            Resources.UnloadUnusedAssets();
            GC.Collect();
#endif
        }

        /// <summary>
        /// 立刻清理GameObject上所有的资源
        /// </summary>
        /// <param name="gameObject"></param>
        public static void ReleaseAssetsImmediate(GameObject gameObject)
        {
#if FUSION_ASSETBUNDLE || !UNITY_EDITOR
            GameObjectAssetRecorder[] assetRecorders = gameObject.GetComponentsInChildren<GameObjectAssetRecorder>(true);
            for (int index = 0; index < assetRecorders.Length; index++)
            {
                assetRecorders[index].ReleaseImmediate();
            }
#else
            Resources.UnloadUnusedAssets();
            GC.Collect();
#endif
        }

        /// <summary>
        /// 销毁GameObject
        /// </summary>
        /// <param name="gameObject"></param>
        public static void Release(GameObject gameObject)
        {
            ReleaseAssets(gameObject);
            UnityEngine.Object.Destroy(gameObject);
        }

        /// <summary>
        /// 立刻销毁GameObject
        /// </summary>
        /// <param name="gameObject"></param>
        public static void ReleaseImmediate(GameObject gameObject)
        {
            ReleaseAssetsImmediate(gameObject);
            UnityEngine.Object.Destroy(gameObject);
        }

        /// <summary>
        /// 通过资源路径释放AssetBundle引用
        /// </summary>
        /// <param name="path">资源路径</param>
        public static void Release(string path, bool persistentAsset = false)
        {
            if (path == null)
            {
                return;
            }


#if FUSION_ASSETBUNDLE || !UNITY_EDITOR
            string assetBundleName = AssetsConfig.GetAssetBundleName(path);
            AssetsManager.Instance.Release(assetBundleName, 1, persistentAsset);
#else
            Resources.UnloadUnusedAssets();
            GC.Collect();
#endif
        }

        /// <summary>
        /// 通过资源路径释放AssetBundle引用 若引用为0立刻销毁
        /// </summary>
        /// <param name="path">资源路径</param>
        public static void ReleaseImmediate(string path, bool persistentAsset = false)
        {
            if (path == null)
            {
                return;
            }

#if FUSION_ASSETBUNDLE || !UNITY_EDITOR
            string assetBundleName = AssetsConfig.GetAssetBundleName(path);
            AssetsManager.Instance.ReleaseImmediate(assetBundleName, 1, persistentAsset);
#else
            Resources.UnloadUnusedAssets();
            GC.Collect();
#endif
        }
    }
}


